package schnorrcomm

import (
	"bytes"
	"crypto/ecdsa"
	Rand "crypto/rand"
	"crypto/sha256"
	"errors"
	"fmt"
	"github.com/wanchain/go-mpc/common"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/crypto/ecies"
	"github.com/wanchain/go-mpc/log"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
)

var BigOne = big.NewInt(1)
var BigZero = big.NewInt(0)

//-------------------for test begin ---------------------

//var PocTest = true
//
//var MaliceRSig = false
//var MaliceRContent = false
//var MaliceSSig = false
//var MaliceSContent = false

//-------------------for test end -----------------------

//---------------------for product begin------------------

var PocTest = false

var MaliceRSig = false
var MaliceRContent = false
var MaliceSSig = false
var MaliceSContent = false

//var MaliceRSigRcv = false
//var MaliceRContentRcv = false
//var MaliceSSigRcv = false
//var MaliceSContentRcv = false

//---------------------for product end------------------

//--------------------------------OpenSlsh or Not ------
var OpenSlsh = false

//--------------------------------OpenSlsh or Not ------

func EncData(pub *ecdsa.PublicKey, m []byte) (ct []byte, err error) {
	pk := ecies.ImportECDSAPublic(pub)
	return ecies.Encrypt(Rand.Reader, pk, m, nil, nil)
}

func DecryptData(prv *ecdsa.PrivateKey, ct []byte) (m []byte, err error) {
	pr := ecies.ImportECDSA(prv)
	return pr.Decrypt(Rand.Reader, ct, nil, nil)
}

func SignInternalData(prv *ecdsa.PrivateKey, hash []byte) (r, s *big.Int, err error) {
	return ecdsa.Sign(Rand.Reader, prv, hash[:])
}

func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
	return crypto.Sign(hash[:], prv)
}

func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
	return crypto.SigToPub(hash[:], sig[:])
}

func Ecrecover(hash, sig []byte) ([]byte, error) {
	return crypto.Ecrecover(hash[:], sig[:])
}

func VerifyInternalData(pub *ecdsa.PublicKey, hash []byte, r, s *big.Int) bool {
	return ecdsa.Verify(pub, hash, r, s)
}

func CheckPK(pk *ecdsa.PublicKey) error {
	if pk == nil {
		return mpcprotocol.ErrInvalidPK
	}
	if !crypto.S256().IsOnCurve(pk.X, pk.Y) {
		return mpcprotocol.ErrInvalidPK
	} else {
		return nil
	}
}

func UintRand(MaxValue uint64) (uint64, error) {
	num, err := Rand.Int(Rand.Reader, new(big.Int).SetUint64(MaxValue))
	if err != nil {
		return 0, err
	}

	return num.Uint64(), nil
}

func BytesToAdd(pubBytes []byte) (common.Address, error) {
	addr := common.BytesToAddress(crypto.Keccak256(pubBytes[0:])[12:])
	return addr, nil
}

func Add04Prefix(b []byte) []byte {
	var ret bytes.Buffer
	ret.Write([]byte{4})
	ret.Write(b)
	return ret.Bytes()
}

// add for schnorr 340 begin
// M||R
func GetSchnorrE(hashBytes, RBytes []byte, mod *big.Int) (*big.Int, error) {
	// compute m=hash(M||R)
	var buffer bytes.Buffer
	buffer.Write(hashBytes[:])
	buffer.Write(RBytes)

	mBytes := sha256.Sum256(buffer.Bytes())
	m := new(big.Int).SetBytes(mBytes[:])
	m = m.Mod(m, mod)
	return m, nil
}

func isOdd(a *big.Int) bool {
	return a.Bit(0) == 1
}

func fromBytesToPk(PKBytes []byte) (*ecdsa.PublicKey, error) {
	if len(PKBytes) != 64 && len(PKBytes) != 65 {
		return nil, errors.New("(sec256)can not UnMarshPt len error")
	}
	var ptBytes bytes.Buffer
	if len(PKBytes) == 64 {
		ptBytes.Write([]byte{4})
	}
	ptBytes.Write(PKBytes)
	pk := crypto.ToECDSAPub(ptBytes.Bytes())
	return pk, nil
}

//for sec-schnorr
func GetParity(RBytes []byte) ([]byte, error) {

	rpk, err := fromBytesToPk(RBytes)
	if err != nil {
		return nil, err
	}

	var parity byte
	parity = 0x1b //27

	if isOdd(rpk.Y) {
		parity = 0x1c //28
	}
	//return common.LeftPadBytes([]byte{parity}, 32), nil
	return []byte{parity}, nil
}

// R_addr||gpk[0]+27-2||gpk.XBytes||message_hash
func GetSchnorrSecE(hashBytes, RBytes, gpkBytes []byte, mod *big.Int) (*big.Int, error) {
	//log.Info("GetSchnorrSecE", "RBytes", common.ToHex(RBytes), "gpkBytes", common.ToHex(gpkBytes), "mod", common.ToHex(mod.Bytes())) //todo should delete

	rpk, err := fromBytesToPk(RBytes)
	if err != nil {
		return nil, err
	}

	RAddrBytes := crypto.PubkeyToAddress(*rpk).Bytes() //20 bytes

	gpk, err := fromBytesToPk(gpkBytes)
	if err != nil {
		return nil, err
	}

	parityByte, _ := GetParity(gpkBytes)

	var buffer bytes.Buffer
	buffer.Write(RAddrBytes)                             //20bytes   (R_addr)
	buffer.Write(parityByte)                             //1byte     (gpk parity)
	buffer.Write(common.LeftPadBytes(gpk.X.Bytes(), 32)) //32bytes   (gpk_x)
	buffer.Write(hashBytes[:])                           //32bytes   (messageHash)

	//mBytes := sha256.Sum256(buffer.Bytes())
	mBytes := crypto.Keccak256(buffer.Bytes())
	m := new(big.Int).SetBytes(mBytes[:])
	m = m.Mod(m, mod)
	log.Info("GetSchnorrSecE",
		"RBytes", common.ToHex(RBytes),
		"gpkBytes", common.ToHex(gpkBytes),
		"RAddrBytes", common.ToHex(RAddrBytes),
		"parityByte", common.ToHex(parityByte),
		"gpk.X", common.ToHex(common.LeftPadBytes(gpk.X.Bytes(), 32)),
		"message", common.ToHex(hashBytes),
		"before sha256", common.ToHex(buffer.Bytes()),
		"after sha256", common.ToHex(mBytes[:]),
		"E", common.ToHex(m.Bytes()),
		"mod", common.ToHex(mod.Bytes())) //todo should delete
	fmt.Printf("RBytes:%v\n,gpkBytes:%v\n,RAddrBytes:%v\n,parityByte:%v\n,gpk.X:%v\n,message:%v\n before hash:%v\n E:%v\n",
		common.ToHex(RBytes), common.ToHex(gpkBytes), common.ToHex(RAddrBytes), common.ToHex(parityByte),
		common.ToHex(common.LeftPadBytes(gpk.X.Bytes(), 32)), common.ToHex(hashBytes), common.ToHex(buffer.Bytes()), common.ToHex(m.Bytes()))
	return m, nil
}

// R(X)||GPK(X)||M
func GetSchnorr340E(hahsBytes, rpkXBytes, gpkXBytes []byte, mod *big.Int) (*big.Int, error) {
	var buf bytes.Buffer
	tag := []byte("BIP0340/challenge")
	buf.Write(rpkXBytes[:])
	buf.Write(gpkXBytes[:])
	buf.Write(hahsBytes[:])

	th := TaggedHash(tag, buf.Bytes())
	retBig := big.NewInt(0).SetBytes(th[:])
	retBig = big.NewInt(0).Mod(retBig, crypto.S256().Params().N)
	return retBig, nil
}

func TaggedHash(tag, msg []byte) [32]byte {
	tagHash := sha256.Sum256(tag[:])

	var buf bytes.Buffer
	buf.Write(tagHash[:])
	buf.Write(tagHash[:])
	buf.Write(msg[:])
	return sha256.Sum256(buf.Bytes())
}

// add for schnorr 340 end
